﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Linq;
using FIFP.DashBoard.Model;
using FIFP.DashBoard.Helper;

namespace FIFP.DashBoard.Calculation
{


    //Capacity : it means the capacity of each Unit, well's capacity is the capacity of child strings; tower's capacity is the capacity of child wells
    //EffectiveCapacity : it only used for tower/pipeline/radial. tower's EC means the tower's capacity plus all other children's capacity
    //                    pipeline's EC means the minimum of its own capacity and its children's EffectiveCapacity
    //Real Capacity : real capacity on specific day
    public class NewCalculation
    {
        #region Properties
        //Input : All units/facilities
        private List<Unit> allUnitList = new List<Unit>();
        public List<Unit> AllUnitList
        {
            get { return allUnitList; }
            set { allUnitList = value; }
        }               

        //Input : shut down plans
        private List<FIFPAppointment> importPlans = new List<FIFPAppointment>();
        public List<FIFPAppointment> ImportPlans
        {
            get { return importPlans; }
            set { importPlans = value; }
        }

        //key : unitID
        //value : list of FIFPAppointment including normal shutdown and linked shutdown
        private Dictionary<int, List<FIFPAppointment>> unitShutdownPlanDictionary = new Dictionary<int, List<FIFPAppointment>>();
        public Dictionary<int, List<FIFPAppointment>> UnitShutdownPlanDictionary
        {
            get { return unitShutdownPlanDictionary; }
            set { unitShutdownPlanDictionary = value; }
        }

        //Input : string capacity
        //key:     UnitID.toString()+CurrentDate.toString()
        //value:   Capacity(in the CurrentDate)
        private Dictionary<string, double> unitCapacityDictionary = new Dictionary<string, double>();
        public Dictionary<string, double> UnitCapacityDictionary
        {
            get { return unitCapacityDictionary; }
            set { unitCapacityDictionary = value; }
        }

        //Input : unit layer relationship
        private Dictionary<string, List<UnitLayer>> layerUnitsDictionary = new Dictionary<string, List<UnitLayer>>();
        public Dictionary<string, List<UnitLayer>> LayerUnitsDictionary
        {
            get { return layerUnitsDictionary; }
            set { layerUnitsDictionary = value; }
        }    
           

        #endregion

        #region Constructor
        public NewCalculation()
        {
            allUnitList = new List<Unit>();
            
        }
        #endregion

        #region Reservoir Capacity Calculation
        
        /// <summary>
        /// get the sum of all oil string to calculate reservoir capacity
        /// </summary>
        /// <param name="currentMonth"></param>
        /// <returns>reservoir capacity for the input month</returns>
        public double CalculateCurrentMonthReservoirCapacity(DateTime currentMonth)
        {
            int numberOfDays = DateTime.DaysInMonth(currentMonth.Year, currentMonth.Month);

            double currentMonthReservoirCapacity = 0;
            List<Unit> oilStringList = getOilStrings();
            foreach(Unit u in oilStringList) {
                currentMonthReservoirCapacity += unitCapacityDictionary[getKeyForStringCapacity(u.UnitID, currentMonth)] * numberOfDays;
            }
            return currentMonthReservoirCapacity;
        }
        #endregion

        #region Effective Capacity Calculation

        public double CalculateCurrentDayEffectiveCapacity(DateTime currentDay)
        {
            foreach (Unit tu in allUnitList)
            {
                tu.EffectiveCapacity = 0;
                tu.RealCapacity = 0;
                tu.IsBottleNeck = false;

                if (tu.UnitType == FIFPConstants.UNITTYPE_TOWER || tu.UnitType == FIFPConstants.UNITTYPE_WELL || tu.UnitType == FIFPConstants.UNITTYPE_STRING)
                    tu.Capacity = 0;
            }

            CalculateWaterStringCurrentDayRealCapacity(currentDay);
            CalculateGasWellCurrentDayRealCapacity(currentDay);
            return CalculateOilNetworkCurrentDayEffectiveCapacity(currentDay);
        }

        #region Water Network Real capacity calculation

        /// <summary>
        /// 
        /// </summary>
        /// <param name="currentDay"></param>
        private void CalculateWaterStringCurrentDayRealCapacity(DateTime currentDay)
        {          
            //water module sum capacity
            List<Unit> waterModuleList = getWaterModules();
            double sumWaterModuleCapacity = getCurrentDayWaterModuleSumCapacity(currentDay, waterModuleList);
            if ( sumWaterModuleCapacity == 0)
            {
                setRealCapacityAsZerotoAllWaterStrings();
                return;
            }

            //calculate water radial sum effective capacity 
            List<Unit> waterRadialList = getWaterRadials();
            double sumWaterRadialEffectiveCapacity = getCurrentDayWaterRadialSumEC(currentDay, waterRadialList);
            
            if (sumWaterRadialEffectiveCapacity == 0 )
            {
                setRealCapacityAsZerotoAllWaterStrings();
            }
            else
            {   //calculate the real capacity for each water string (radial,pipeline)
                if (sumWaterModuleCapacity < sumWaterRadialEffectiveCapacity)
                {
                    foreach (Unit wr in waterRadialList)
                    {
                        wr.RealCapacity = (wr.EffectiveCapacity / sumWaterRadialEffectiveCapacity) * sumWaterModuleCapacity;
                        DistributeFromWaterRadialtoString(wr);
                    }
                }
                else
                {                    
                    foreach (Unit wr in waterRadialList)
                    {
                        wr.RealCapacity = wr.EffectiveCapacity;
                        DistributeFromWaterRadialtoString(wr);
                    }
                }
            }
        }

        private double getCurrentDayWaterModuleSumCapacity(DateTime currentDay, List<Unit> waterModuleList)
        {
            double sumWaterModuleCapacity = 0;
            ReducedCapacityHelper rHelper = new ReducedCapacityHelper();
            foreach (Unit wm in waterModuleList)
            {
                //if water module is not shutdown
                if (isUnitShutdown(wm, currentDay, rHelper) == false)
                {
                    sumWaterModuleCapacity += wm.Capacity;
                    wm.RealCapacity = wm.Capacity;
                }
                else
                {
                    if (rHelper.ReducedCapacity == 0)
                        wm.RealCapacity = 0;
                    else
                        wm.RealCapacity = wm.Capacity - rHelper.ReducedCapacity;

                    sumWaterModuleCapacity += wm.RealCapacity;
                }
            }
            return sumWaterModuleCapacity;
        }

        private void setRealCapacityAsZerotoAllWaterStrings()
        {
            List<Unit> waterStrings = getWaterStrings();
            foreach (Unit ws in waterStrings)
            {
                ws.RealCapacity = 0;
            }
        }

        /// <summary>
        /// calculate the sum of effective capacity for every water radial at currentday
        /// </summary>
        /// <param name="currentDay"></param>
        /// <param name="waterRadialList"></param>
        /// <returns></returns>
        private double getCurrentDayWaterRadialSumEC(DateTime currentDay, List<Unit> waterRadialList)
        {
            double sumWaterRadialEffectiveCapacity = 0;
            foreach (Unit wr in waterRadialList)
            {
                WaterNodeEffectiveCapacityCalculation(wr, currentDay);
                sumWaterRadialEffectiveCapacity += wr.EffectiveCapacity;
            }
            return sumWaterRadialEffectiveCapacity;
        }

        /// <summary>
        /// recursively calculate effective capacity for one water radial at currentday
        /// the algrithrom considers partial shutdown
        /// </summary>
        /// <param name="node"></param>
        /// <param name="currentDay"></param>
        public void WaterNodeEffectiveCapacityCalculation(Unit node, DateTime currentDay)
        {
            if (isLeafNode(node))
            {
                List<Unit> wells = node.ChildUnitList;
                foreach (Unit well in wells)
                {
                    setCurrentDayWellAndStringCapacity(well, currentDay);
                    node.EffectiveCapacity += well.Capacity;
                    node.Capacity += well.Capacity;                    
                }
            }
            else
            {
                if (node.UnitType == FIFPConstants.UNITTYPE_TOWER)
                {
                    List<Unit> notWellChildren = getChildWithoutWells(node);
                    foreach (Unit child in notWellChildren)
                    {
                        //recursively calculate child EC
                        WaterNodeEffectiveCapacityCalculation(child, currentDay);
                        //add child EC to tower EC
                        node.EffectiveCapacity += child.EffectiveCapacity;
                    }

                    List<Unit> wells = getChildWaterWells(node);
                    foreach (Unit well in wells)
                    {
                        setCurrentDayWellAndStringCapacity(well, currentDay);
                        node.EffectiveCapacity += well.Capacity;
                        node.Capacity += well.Capacity;
                    }
                }
                else if (node.UnitType == FIFPConstants.UNITTYPE_RADIAL || node.UnitType == FIFPConstants.UNITTYPE_PIPELINE)
                {
                    List<Unit> children = node.ChildUnitList;
                    double childrenEffectiveCapacity = 0;                   
                    foreach (Unit child in children)
                    {
                        //recursively calculate child EC
                        WaterNodeEffectiveCapacityCalculation(child, currentDay);
                        childrenEffectiveCapacity += child.EffectiveCapacity;                        
                    }

                    //check bottleneck
                    ReducedCapacityHelper rHelper = new ReducedCapacityHelper();
                    if (isUnitShutdown(node, currentDay, rHelper))
                    {
                        if (rHelper.ReducedCapacity != 0)
                        {
                            double currentDayCapacity = node.Capacity - rHelper.ReducedCapacity;
                            node.EffectiveCapacity = Math.Min(currentDayCapacity, childrenEffectiveCapacity);
                        }
                        else
                            node.EffectiveCapacity = 0;
                        //node.IsBottleNeck = true;
                    }
                    else
                    {
                        //pipeline capacity not big enough, maybe caused by pipeline partial shutdown
                        node.EffectiveCapacity = Math.Min(node.Capacity, childrenEffectiveCapacity);
                        //if (node.Capacity < childrenEffectiveCapacity)
                        //    node.IsBottleNeck = true;
                    }                   
                }               
            }
        }       

        /// <summary>
        /// distribute water(real capacity) from radial to the below level and
        /// the algrithrom considers partial shutdown
        /// </summary>
        /// <param name="node"></param>
        private void DistributeFromWaterRadialtoString(Unit node)
        {            
            List<Unit> children = node.ChildUnitList;

            double sumEffectiveCapacityForChildren = 0;
            foreach (Unit u in children)
            {
                sumEffectiveCapacityForChildren += u.EffectiveCapacity;
            }

            foreach (Unit u in children)
            {
                if (sumEffectiveCapacityForChildren == 0)
                    u.RealCapacity = 0;
                else
                    u.RealCapacity = (u.EffectiveCapacity / sumEffectiveCapacityForChildren) * node.RealCapacity;

                //assume only tower connected to radial or pipeline   
                if (u.UnitType == FIFPConstants.UNITTYPE_TOWER)
                {
                    List<Unit> allChildren = u.ChildUnitList;
                    List<Unit> allWells = getChildWells(u);
                    List<Unit> allWaterWells = getChildWaterWells(u);
                    
                    //if it is leaf tower
                    if (allChildren.Count == allWells.Count)
                    {
                        double tempSumValue = 0;
                        foreach (Unit w in allWaterWells)
                        {
                            tempSumValue += w.Capacity;
                        }

                        foreach (Unit w in allWaterWells)
                        {
                            if (tempSumValue == 0)
                                w.RealCapacity = 0;
                            else
                                w.RealCapacity = (w.Capacity / tempSumValue) * u.RealCapacity;
                            DistributeFromWelltoString(w);
                        }
                    }
                    //if it is not leaf tower
                    else
                    {
                        double tempSumValue = 0;
                        foreach (Unit w in allChildren)
                        {
                            if (w.UnitType == FIFPConstants.UNITTYPE_WELL)
                                tempSumValue += w.Capacity;
                            else if (w.UnitType == FIFPConstants.UNITTYPE_PIPELINE)
                                tempSumValue += w.EffectiveCapacity;
                        }

                        foreach (Unit c in allChildren)
                        {
                            if (c.UnitType == FIFPConstants.UNITTYPE_WELL)
                            {
                                if (tempSumValue == 0)
                                    c.RealCapacity = 0;
                                else
                                    c.RealCapacity = (c.Capacity / tempSumValue) * u.RealCapacity;
                                DistributeFromWelltoString(c);
                            }
                            else if (c.UnitType == FIFPConstants.UNITTYPE_PIPELINE)
                            {
                                if (tempSumValue == 0)
                                    c.RealCapacity = 0;
                                else
                                    c.RealCapacity = (c.EffectiveCapacity / tempSumValue) * u.RealCapacity;
                            }
                        }

                        foreach (Unit p in allChildren)
                        {
                            if (p.UnitType == FIFPConstants.UNITTYPE_PIPELINE)
                            {
                                DistributeFromWaterRadialtoString(p);
                            }
                        }
                    }
                }
            }
        }

        private void DistributeFromWelltoString(Unit well)
        {
            if (well.RealCapacity == 0)
            {
                foreach (Unit s in well.ChildUnitList)
                {
                    s.RealCapacity = 0;
                }
                return;
            }

            double tempSumValue = 0;
            foreach (Unit s in well.ChildUnitList)
            {
                tempSumValue += s.Capacity;
            }

            if (tempSumValue == 0)
            {
                foreach (Unit s in well.ChildUnitList)
                {
                    s.RealCapacity = 0;
                }
            }
            else
            {
                foreach (Unit s in well.ChildUnitList)
                {
                    s.RealCapacity = (s.Capacity / tempSumValue) * well.RealCapacity;
                }
            }
        }

        #endregion

        #region Gas well Real capacity calculation
        private void CalculateGasWellCurrentDayRealCapacity(DateTime currentDay)
        {
            //compressor sum capacity
            List<Unit> gasCompressorList = getGasCompressors();
            double sumGasCompressorCapacity = getCurrentDayGasCompressorSumCapacity(currentDay, gasCompressorList);

            List<Unit> autoInjectionTowerList = getAutoInjectionTowers();
            if (sumGasCompressorCapacity == 0)
            {
                autoInjectionTowerList = getGasTowers();
            }
            else
            {
                //GIF gas radial sum effective capacity 
                List<Unit> gasRadialList = getGasRadials();
                double sumGasRadialEffectiveCapacity = getCurrentDayGasRadialSumEC(currentDay, gasRadialList);

                if (sumGasRadialEffectiveCapacity == 0)
                {
                    List<Unit> gasWells = getGasWells();
                    foreach (Unit gw in gasWells)
                    {
                        gw.RealCapacity = 0;
                    }
                }
                else
                {
                    if (sumGasCompressorCapacity < sumGasRadialEffectiveCapacity)
                    {
                        foreach (Unit gr in gasRadialList)
                        {
                            gr.RealCapacity = (gr.EffectiveCapacity / sumGasRadialEffectiveCapacity) * sumGasCompressorCapacity;
                            DistributeFromGasRadialtoWell(gr);
                        }
                    }
                    else
                    {
                        foreach (Unit gr in gasRadialList)
                        {
                            gr.RealCapacity = gr.EffectiveCapacity;
                            DistributeFromGasRadialtoWell(gr);
                        }
                    }
                }
            }

            //add auto injection gas well real capacity calculation
            foreach (Unit t in autoInjectionTowerList)
            {
                List<Unit> gasInjectionWellList = (from item in t.ChildUnitList
                                                   where item.ProdInjType == "P"
                                                   select item).ToList<Unit>();

                foreach (Unit w in gasInjectionWellList)
                {
                    w.RealCapacity = getCurrentDayGasWellCapacity(w, currentDay);
                }
            }

        }

        private double getCurrentDayGasCompressorSumCapacity(DateTime currentDay, List<Unit> gasCompressorList)
        {
            double sumGasCompressorCapacity = 0;
            ReducedCapacityHelper rHelper = new ReducedCapacityHelper();
            foreach (Unit gc in gasCompressorList)
            {
                if (isUnitShutdown(gc, currentDay, rHelper) == false)
                {
                    sumGasCompressorCapacity += gc.Capacity;
                    gc.RealCapacity = gc.Capacity;
                }
                else
                {
                    if (rHelper.ReducedCapacity != 0)
                        gc.RealCapacity = gc.Capacity - rHelper.ReducedCapacity;
                    else
                        gc.RealCapacity = 0;
                }
            }
            return sumGasCompressorCapacity;
        }

        private double getCurrentDayGasRadialSumEC(DateTime currentDay, List<Unit> gasRadialList)
        {
            double sumGasRadialEffectiveCapacity = 0;
            foreach (Unit gr in gasRadialList)
            {
                GasNodeEffectiveCapacityCalculation(gr, currentDay);
                sumGasRadialEffectiveCapacity += gr.EffectiveCapacity;
            }
            return sumGasRadialEffectiveCapacity;
        }

        public void GasNodeEffectiveCapacityCalculation(Unit node, DateTime currentDay)
        {
            if (isLeafNode(node))
            {
                List<Unit> wells = node.ChildUnitList;
                foreach (Unit well in wells)
                {
                    well.Capacity = getCurrentDayGasWellCapacity(well, currentDay);
                    node.EffectiveCapacity += well.Capacity;
                    node.Capacity += well.Capacity;
                }
            }
            else
            {
                if (node.UnitType == FIFPConstants.UNITTYPE_TOWER)
                {
                    List<Unit> notWellChildren = getChildWithoutWells(node);
                    foreach (Unit child in notWellChildren)
                    {
                        //recursively calculate child EC
                        GasNodeEffectiveCapacityCalculation(child, currentDay);
                        //add child EC to tower EC
                        node.EffectiveCapacity += child.EffectiveCapacity;
                    }

                    List<Unit> wells = getChildWaterWells(node);
                    foreach (Unit well in wells)
                    {
                        setCurrentDayWellAndStringCapacity(well, currentDay);
                        //add well EC to tower EC
                        node.EffectiveCapacity += well.Capacity;
                        //add well C to tower C
                        node.Capacity += well.Capacity;
                    }
                }
                else if (node.UnitType == FIFPConstants.UNITTYPE_RADIAL || node.UnitType == FIFPConstants.UNITTYPE_PIPELINE)
                {                                       
                    List<Unit> children = node.ChildUnitList;
                    double childrenEffectiveCapacity = 0;
                    foreach (Unit child in children)
                    {
                        //recursively calculate child EC
                        GasNodeEffectiveCapacityCalculation(child, currentDay);
                        childrenEffectiveCapacity += child.EffectiveCapacity;
                    }

                    //pipeline capacity not big enough, maybe caused by pipeline partial shutdown
                    ReducedCapacityHelper rHelper = new ReducedCapacityHelper();
                    if (isUnitShutdown(node, currentDay, rHelper))
                    {
                        if (rHelper.ReducedCapacity == 0)
                            node.EffectiveCapacity = 0;
                        else
                        {
                            double currentDayCapacity = node.Capacity - rHelper.ReducedCapacity;
                            node.EffectiveCapacity = Math.Min(currentDayCapacity, childrenEffectiveCapacity);
                        }
                    }
                    else
                    {
                        node.EffectiveCapacity = Math.Min(node.Capacity, childrenEffectiveCapacity);
                    }                    
                }
            }
        }

        private void DistributeFromGasRadialtoWell(Unit radial)
        {
            //according to current gas structure,  only 1 tower connected to radial    
            List<Unit> children = radial.ChildUnitList;
            Unit tower = children[0];
            tower.RealCapacity = radial.RealCapacity;

            List<Unit> wells = tower.ChildUnitList;
            double tempSumValue = 0;
            foreach (Unit w in wells)
            {
                tempSumValue += w.Capacity;
            }
            foreach (Unit w in wells)
            {
                if (tempSumValue == 0)
                    w.RealCapacity = 0;
                else
                {
                    w.RealCapacity = (w.Capacity / tempSumValue) * tower.RealCapacity;
                    //transfer unit from gas to water
                    //w.RealCapacity = w.RealCapacity / ?;
                }
             }               
        }

        #endregion

        #region Oil Network effective capacity Calculation
        private double CalculateOilNetworkCurrentDayEffectiveCapacity(DateTime currentDay)
        {            
            SetOilStringCapacityAfterApplyShutdown(currentDay);           
            CalculateOilStringCurrentDayRealCapacityByLayerInjectionCompare(currentDay);

            //need to add check that tower with two parent radials

            //HashSet<Unit> radialPlatforms = new HashSet<Unit>();
            List<Unit> oilRadialList = getOilRadials();
            foreach (Unit oilRadial in oilRadialList)
            {
                OilNodeEffectiveCapacityCalculation(oilRadial, currentDay);
                //radialPlatforms.Add(getUnitByUID(oilRadial.ParentUnitID));
            }
            return CalculateOilNetworkEffectiveCapacityHardCode(currentDay,oilRadialList);
            //CalculateOilNetworkEffectiveCapacity(radialPlatforms.ToList<Unit>());
        }

        /// <summary>
        /// set oil string capacity consider shutdown, if shutdown, capacity is zero
        /// </summary>
        /// <param name="currentDay"></param>
        private void SetOilStringCapacityAfterApplyShutdown(DateTime currentDay)
        {
            List<Unit> oilStringList = getOilStrings();
            foreach (Unit oilString in oilStringList)
            {
                double strCapacity = getCurrentDayStringCapacity(oilString, currentDay);
                oilString.Capacity = strCapacity;
            }
        }

        /// <summary>
        ///  compare injection and production in each layer, and set oil string real capacity
        /// </summary>
        /// <param name="currentDay"></param>
        private void CalculateOilStringCurrentDayRealCapacityByLayerInjectionCompare(DateTime currentDay)
        {
            List<string> layerNameList = layerUnitsDictionary.Keys.ToList<string>();

            List<Unit> layerProductorList = new List<Unit>();
            //List<Unit> injectorList = new List<Unit>();
            double sumLayerInjectorCapacity = 0;
            double sumLayerProductorCapacity = 0;

            foreach (string layerName in layerNameList)
            {
                //non arab layer producers real capacity
                if (layerName.Contains("NON ARAB"))
                {
                    List<UnitLayer> ulList = layerUnitsDictionary[layerName];
                    foreach (UnitLayer ul in ulList)
                    {
                        Unit u = getUnitByUID(ul.unitID);
                        u.RealCapacity = u.Capacity;
                    }
                }
                else
                {
                    List<UnitLayer> ulList = layerUnitsDictionary[layerName];

                    foreach (UnitLayer ul in ulList)
                    {
                        Unit u = getUnitByUID(ul.unitID);
                        //each producer belongs to only one layer
                        if (u.FluidType == FIFPConstants.FLUIDTYPE_OIL)
                        {
                            layerProductorList.Add(u);
                            sumLayerProductorCapacity += u.Capacity * ul.percentage;
                        }
                        else if(u.FluidType == FIFPConstants.FLUIDTYPE_WATER)
                        {                            
                            //unit transfer from MBWD(water unit) to  MBOD(oil unit)
                            sumLayerInjectorCapacity += u.RealCapacity * ul.percentage * 0.5;
                            //injectorList.Add(u);
                        }
                        else if (u.FluidType == FIFPConstants.FLUIDTYPE_GAS)
                        {
                            //unit transfer from MMSCFD(gas unit) to MBOD(oil unit)
                            sumLayerInjectorCapacity += u.RealCapacity * ul.percentage * 0.6298 * 0.5;
                            //injectorList.Add(u);
                        }
                    }

                    if (layerProductorList.Count == 0)
                        continue;

                    //later need to apply layer IPR
                    double IPR = 1.75;

                    //layer injection and production compare
                    //pre : Each production string only in one layer
                    if (sumLayerProductorCapacity < sumLayerInjectorCapacity / IPR)
                    {
                        foreach (Unit productor in layerProductorList)
                        {
                            productor.RealCapacity = productor.Capacity;
                        }
                    }
                    else
                    {
                        foreach (Unit productor in layerProductorList)
                        {
                            productor.RealCapacity = (productor.Capacity / sumLayerProductorCapacity) * sumLayerInjectorCapacity;
                        }
                    }

                    sumLayerInjectorCapacity = 0;
                    sumLayerProductorCapacity = 0;
                    layerProductorList.Clear();
                    //injectorList.Clear();
                }
            }
        }

        public void OilNodeEffectiveCapacityCalculation(Unit node, DateTime currentDay)
        {
            if (isLeafNode(node))
            {
                List<Unit> wells = node.ChildUnitList;
                foreach (Unit well in wells)
                {
                    setCurrentDayOilWellCapacity(well, currentDay);
                    node.EffectiveCapacity += well.Capacity;
                    node.Capacity += well.Capacity;
                }
            }
            else
            {
                if (node.UnitType == FIFPConstants.UNITTYPE_TOWER)
                {
                    List<Unit> notWellChildren = getChildWithoutWells(node);
                    foreach (Unit child in notWellChildren)
                    {
                        //recursively calculate child EC
                        OilNodeEffectiveCapacityCalculation(child, currentDay);
                        //add child EC to tower EC
                        node.EffectiveCapacity += child.EffectiveCapacity;
                    }

                    List<Unit> wells = getChildOilWells(node);
                    foreach (Unit well in wells)
                    {
                        setCurrentDayOilWellCapacity(well, currentDay);
                        //add well EC to tower EC
                        node.EffectiveCapacity += well.Capacity;
                        //add well C to tower C
                        node.Capacity += well.Capacity;
                    }
                }
                else if (node.UnitType == FIFPConstants.UNITTYPE_RADIAL || node.UnitType == FIFPConstants.UNITTYPE_PIPELINE)
                {
                    List<Unit> children = node.ChildUnitList;
                    double childrenEffectiveCapacity = 0;
                    foreach (Unit child in children)
                    {
                        //recursively calculate child EC
                        OilNodeEffectiveCapacityCalculation(child, currentDay);
                        childrenEffectiveCapacity += child.EffectiveCapacity;
                    }

                    ReducedCapacityHelper rHelper = new ReducedCapacityHelper();
                    if (isUnitShutdown(node, currentDay, rHelper))
                    {
                        if (rHelper.ReducedCapacity == 0)
                            node.EffectiveCapacity = 0;
                        else
                        {
                            double currentDayCapacity = node.Capacity - rHelper.ReducedCapacity;
                            node.EffectiveCapacity = Math.Min(currentDayCapacity, childrenEffectiveCapacity);
                        }                        
                        //node.IsBottleNeck = true;
                    }
                    else
                    {
                        //pipeline capacity not big enough, maybe caused by pipeline partial shutdown
                        node.EffectiveCapacity = Math.Min(node.Capacity, childrenEffectiveCapacity);
                        //if (node.Capacity < childrenEffectiveCapacity)
                            node.IsBottleNeck = true;
                    }
                }
                //else if (node.UnitType == FIFPConstants.UNITTYPE_PLATFORM)
                //{
                //    List<Unit> children = node.ChildUnitList;
                //    foreach (Unit child in children)
                //    {
                //        //recursively calculate child radial EC
                //        OilNodeEffectiveCapacityCalculation(child, currentDay);
                //        node.EffectiveCapacity += child.EffectiveCapacity;
                //    }
                //}
            }
        }

        private void setCurrentDayOilWellCapacity(Unit well, DateTime currentDay)
        {
            double wellCapacity = 0;
            foreach (Unit str in well.ChildUnitList)
            {
                wellCapacity += str.RealCapacity;
            }
            well.Capacity = wellCapacity;
            well.EffectiveCapacity = wellCapacity;
        }

        private double CalculateOilNetworkEffectiveCapacityHardCode(DateTime currentDay, List<Unit> oilRadialList)
        {
            double result = 0;

            foreach (Unit r in oilRadialList)
            {
                result += r.EffectiveCapacity;
            }

            result = Math.Min(result, getCurrentDayByPassCapacity(currentDay) + getCurrentDaySeperatorsCapacity(currentDay));
            result = Math.Min(result, getCurrentDayMOLCapacity(currentDay));
            return result;
        }

        private double getCurrentDayByPassCapacity(DateTime currentDay)
        {
            double result = 0;
            Unit bypassline = (from item in allUnitList
                               where item.UnitName == "ByPassLine"
                               select item).ToList<Unit>()[0];

            ReducedCapacityHelper rHelper = new ReducedCapacityHelper();
            if(isUnitShutdown(bypassline, currentDay,rHelper) == true)
            {
                if(rHelper.ReducedCapacity == 0)
                    result = 0;
                else
                    result = bypassline.Capacity - rHelper.ReducedCapacity;

            } else
            {
                result = bypassline.Capacity;
            }

            return result;
        }

        private double getCurrentDaySeperatorsCapacity(DateTime currentDay)
        {
            double result = 0;
            List<Unit> separatorList = (from item in allUnitList
                                        where item.UnitType == FIFPConstants.UNITTYPE_SEPERATOR
                                        select item).ToList<Unit>();

            foreach (Unit separator in separatorList)
            {
                ReducedCapacityHelper rHelper = new ReducedCapacityHelper();
                if (isUnitShutdown(separator, currentDay, rHelper) == true)
                {
                    if (rHelper.ReducedCapacity == 0)
                        result += 0;
                    else
                        result += (separator.Capacity - rHelper.ReducedCapacity);

                }
                else
                {
                    result += separator.Capacity;
                }
            }
            return result;
        }

        private double getCurrentDayMOLCapacity(DateTime currentDay)
        {
            double result = 0;
            Unit bypassline = (from item in allUnitList
                               where item.UnitName == "MOL"
                               select item).ToList<Unit>()[0];

            ReducedCapacityHelper rHelper = new ReducedCapacityHelper();
            if (isUnitShutdown(bypassline, currentDay, rHelper) == true)
            {
                if (rHelper.ReducedCapacity == 0)
                    result = 0;
                else
                    result = bypassline.Capacity - rHelper.ReducedCapacity;

            }
            else
            {
                result = bypassline.Capacity;
            }

            return result;
        }

        /// <summary>
        /// recursively calculate capacity by level
        /// </summary>
        /// <param name="unitList"></param>
        /// <returns></returns>
        private double CalculateOilNetworkEffectiveCapacity(List<Unit> unitList)
        {
            double result = 0;
            double sumCapacity = 0;

            HashSet<int> parentUnitIDSet = new HashSet<int>();
            foreach (Unit unit in unitList)
            {
                // need to change to apply shutdown and get current day capacity
                sumCapacity += unit.Capacity;
                foreach (int id in unit.ParentUnitIDList)
                    parentUnitIDSet.Add(id);
            }

            int rootNode = 0;
            foreach (int i in parentUnitIDSet)
            {
                rootNode += i;
            }

            //check root node
            if (rootNode == -1)
            {
                result = sumCapacity;
            }
            else
            {
                List<Unit> parentList = new List<Unit>();
                foreach (int pId in parentUnitIDSet)
                {
                    Unit unit = getUnitByUID(pId);
                    parentList.Add(unit);
                }
                result = Math.Min(sumCapacity, CalculateOilNetworkEffectiveCapacity(parentList));
            }
            return result;
        }

        #endregion

        #endregion

        #region Methods
        //leaf node means this unit is a tower and has only well as child
        private bool isLeafNode(Unit node)
        {
            bool isLeafNode = false;

            if (node.UnitType != FIFPConstants.UNITTYPE_TOWER)
            {
                isLeafNode = false;
            }
            else
            {
                if (hasPipelineAsChild(node))
                {
                    isLeafNode = false;
                }
                else
                {
                    isLeafNode = true;
                }
            }

            return isLeafNode;
        }

        private bool hasPipelineAsChild(Unit node)
        {
            bool hasPipelineAsChild = false;

            List<Unit> resultList = (from item in node.ChildUnitList
                                     where item.UnitType == FIFPConstants.UNITTYPE_PIPELINE 
                                     select item).ToList<Unit>();

            if (resultList.Count != 0)
                hasPipelineAsChild = true;

            return hasPipelineAsChild;
        }
    
        private void setCurrentDayWellAndStringCapacity(Unit well, DateTime currentDay)
        {
            double wellCapacity = 0;
            foreach(Unit str in well.ChildUnitList)
            {
                double strCapacity = getCurrentDayStringCapacity(str, currentDay);
                str.Capacity = strCapacity;
                wellCapacity += strCapacity;
            }
            well.Capacity = wellCapacity;
        }        

        public double getCurrentDayStringCapacity(Unit unit, DateTime currentDay)
        {
            ReducedCapacityHelper rHelper = new ReducedCapacityHelper();

            DateTime firstDayOftheCurrentMonth = new DateTime(currentDay.Year, currentDay.Month, 1);
            string key = getKeyForStringCapacity(unit.UnitID, firstDayOftheCurrentMonth);
            double stringCurrentDayCapacity = unitCapacityDictionary[key];

            if (isUnitShutdown(unit, currentDay, rHelper))
            {
                if (rHelper.ReducedCapacity != 0)
                    stringCurrentDayCapacity = stringCurrentDayCapacity - rHelper.ReducedCapacity;
                else if (rHelper.CapacityPercentage != 1)
                    stringCurrentDayCapacity = stringCurrentDayCapacity * rHelper.CapacityPercentage;
                else
                    stringCurrentDayCapacity = 0;
            }

            return stringCurrentDayCapacity;
        }

        private double getCurrentDayGasWellCapacity(Unit well, DateTime currentDay)
        {
            DateTime firstDayOftheCurrentYear = new DateTime(currentDay.Year, 1, 1);
            string key = getKeyForStringCapacity(well.UnitID, firstDayOftheCurrentYear);
            double gasWellCurrentDayCapacity = unitCapacityDictionary[key];

            ReducedCapacityHelper rHelper = new ReducedCapacityHelper();
            if (isUnitShutdown(well, currentDay, rHelper))
            {
                if (rHelper.ReducedCapacity != 0)
                    gasWellCurrentDayCapacity = gasWellCurrentDayCapacity - rHelper.ReducedCapacity;
                else
                    gasWellCurrentDayCapacity = 0;
            }

            return gasWellCurrentDayCapacity;
        }
                
        private bool isUnitShutdown(Unit unit, DateTime currentDay, ReducedCapacityHelper rHelper)
        {
            bool isUnitShutdown = false;

            if (unitShutdownPlanDictionary.ContainsKey(unit.UnitID))
            {
                List<FIFPAppointment> unitShutDownPlans = unitShutdownPlanDictionary[unit.UnitID];
                foreach (FIFPAppointment p in unitShutDownPlans)
                {
                    if (currentDay >= p.Start && currentDay <= p.End)
                    {
                        isUnitShutdown = true;
                        rHelper.CapacityPercentage = p.CapacityPercentage;
                        rHelper.ReducedCapacity = p.ReducedCapacity;
                        return isUnitShutdown;
                    }
                }
            }
            return isUnitShutdown;
        }

        private string getKeyForStringCapacity(int sUID, DateTime currentDay)
        {
            return sUID.ToString() + currentDay.ToString();
        }

        public Unit getUnitByUID(int uID)
        {
            List<Unit> uList = (from item in allUnitList
                                where item.UnitID == uID
                                select item).ToList<Unit>();
            if (uList.Count == 0)
                return null;
            else
                return uList[0];
        }

        private List<Unit> getPlatforms()
        {
            return (from item in allUnitList
                    where item.UnitType == FIFPConstants.UNITTYPE_PLATFORM
                    select item).ToList<Unit>();
        }

        private List<Unit> getWaterModules()
        {
            return (from item in allUnitList
                    where item.UnitType == FIFPConstants.UNITTYPE_MODULE 
                    select item).ToList<Unit>();
        }

        private List<Unit> getWaterRadials()
        {
            return (from item in allUnitList
                    where item.UnitType == FIFPConstants.UNITTYPE_RADIAL && item.FluidType == FIFPConstants.FLUIDTYPE_WATER
                    select item).ToList<Unit>();
        }

        private List<Unit> getGasCompressors()
        {
            return (from item in allUnitList
                    where item.UnitType == FIFPConstants.UNITTYPE_COMPRESSOR
                    select item).ToList<Unit>();
        }

        private List<Unit> getGasRadials()
        {
            return (from item in allUnitList
                    where item.UnitType == FIFPConstants.UNITTYPE_RADIAL && item.FluidType == FIFPConstants.FLUIDTYPE_GAS
                    select item).ToList<Unit>();
        }

        private List<Unit> getGasWells()
        {
            return (from item in allUnitList
                    where item.UnitType == FIFPConstants.UNITTYPE_WELL && item.FluidType == FIFPConstants.FLUIDTYPE_GAS
                    select item).ToList<Unit>();
        }


        private List<Unit> getGasTowers()
        {
            return (from item in allUnitList
                    where item.UnitType == FIFPConstants.UNITTYPE_TOWER && item.FluidType == FIFPConstants.FLUIDTYPE_GAS
                    select item).ToList<Unit>();
        }

        private List<Unit> getAutoInjectionTowers()
        {
            return (from item in allUnitList
                    where item.UnitType == FIFPConstants.UNITTYPE_TOWER && item.FluidType == FIFPConstants.FLUIDTYPE_GAS && item.ParentUnitID == -2
                    select item).ToList<Unit>();
        }


        private List<Unit> getOilRadials()
        {
            return (from item in allUnitList
                    where item.UnitType == FIFPConstants.UNITTYPE_RADIAL && item.FluidType == FIFPConstants.FLUIDTYPE_OIL
                    select item).ToList<Unit>();
        }

        private List<Unit> getAllStrings()
        {
            return (from item in allUnitList
                    where item.UnitType == FIFPConstants.UNITTYPE_STRING
                    select item).ToList<Unit>();
        }

        private List<Unit> getWaterStrings()
        {
            return (from item in allUnitList
                    where item.UnitType == FIFPConstants.UNITTYPE_STRING && item.FluidType == FIFPConstants.FLUIDTYPE_WATER
                    select item).ToList<Unit>();
        }

        private List<Unit> getOilStrings()
        {
            return (from item in allUnitList
                    where item.UnitType == FIFPConstants.UNITTYPE_STRING && item.FluidType == FIFPConstants.FLUIDTYPE_OIL
                    select item).ToList<Unit>();
        }

        private List<Unit> getChildWaterWells(Unit unit)
        {
            List<Unit> children = (from item in unit.ChildUnitList
                                   where item.UnitType == FIFPConstants.UNITTYPE_WELL && item.FluidType == FIFPConstants.FLUIDTYPE_WATER
                                   select item).ToList<Unit>();
            return children;
        }

        private List<Unit> getChildOilWells(Unit unit)
        {
            List<Unit> children = (from item in unit.ChildUnitList
                                   where item.UnitType == FIFPConstants.UNITTYPE_WELL && item.FluidType == FIFPConstants.FLUIDTYPE_OIL
                                   select item).ToList<Unit>();
            return children;
        }

        private List<Unit> getChildWells(Unit unit)
        {
            List<Unit> children = (from item in unit.ChildUnitList
                                   where item.UnitType == FIFPConstants.UNITTYPE_WELL
                                   select item).ToList<Unit>();
            return children;
        }

        private List<Unit> getChildWithoutWells(Unit unit)
        {
            List<Unit> children = (from item in unit.ChildUnitList
                                   where item.UnitType != FIFPConstants.UNITTYPE_WELL
                                   select item).ToList<Unit>();
            return children;
        }
        #endregion

        #region Dummy data
        public void LoadUnitsS4WithStrings()
        {
            allUnitList.Clear();

            //Oil Units
            allUnitList.Add(new Unit() { ParentUnitID = -1, UnitID = 0, UnitName = "Radial1", UnitType = FIFPConstants.UNITTYPE_RADIAL, FluidType = FIFPConstants.FLUIDTYPE_OIL, Capacity = 1000 });
            allUnitList.Add(new Unit() { ParentUnitID = 0, UnitID = 1, UnitName = "Pipeline1", UnitType = FIFPConstants.UNITTYPE_PIPELINE, FluidType = FIFPConstants.FLUIDTYPE_OIL, Capacity = 100 });
            allUnitList.Add(new Unit() { ParentUnitID = 1, UnitID = 2, UnitName = "Tower1", UnitType = FIFPConstants.UNITTYPE_TOWER, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            allUnitList.Add(new Unit() { ParentUnitID = 2, UnitID = 12, UnitName = "Well11", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            Unit string11_1 = new Unit() { ParentUnitID = 12, UnitID = 41, UnitName = "String11/1", UnitType = FIFPConstants.UNITTYPE_STRING, FluidType = FIFPConstants.FLUIDTYPE_OIL };
            allUnitList.Add(string11_1);
            unitCapacityDictionary.Add(getKeyForStringCapacity(string11_1.UnitID, new DateTime(2013, 1, 1)), 100);
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D2",  unitID =string11_1.UnitID, percentage = 1 });


            allUnitList.Add(new Unit() { ParentUnitID = 0, UnitID = 3, UnitName = "Pipeline2", UnitType = FIFPConstants.UNITTYPE_PIPELINE, Capacity = 300, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            allUnitList.Add(new Unit() { ParentUnitID = 3, UnitID = 4, UnitName = "Tower2", UnitType = FIFPConstants.UNITTYPE_TOWER, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            allUnitList.Add(new Unit() { ParentUnitID = 4, UnitID = 13, UnitName = "Well21", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            allUnitList.Add(new Unit() { ParentUnitID = 4, UnitID = 14, UnitName = "Well22", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            Unit string21_1 = new Unit() { ParentUnitID = 13, UnitID = 42, UnitName = "String21/1", UnitType = FIFPConstants.UNITTYPE_STRING, FluidType = FIFPConstants.FLUIDTYPE_OIL };
            Unit string22_1 = new Unit() { ParentUnitID = 14, UnitID = 43, UnitName = "String22/1", UnitType = FIFPConstants.UNITTYPE_STRING, FluidType = FIFPConstants.FLUIDTYPE_OIL }; 
            allUnitList.Add(string21_1);
            allUnitList.Add(string22_1);
            unitCapacityDictionary.Add(getKeyForStringCapacity(string21_1.UnitID, new DateTime(2013, 1, 1)), 100);
            unitCapacityDictionary.Add(getKeyForStringCapacity(string22_1.UnitID, new DateTime(2013, 1, 1)), 150);
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D3U",  unitID = string21_1.UnitID, percentage = 1 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D3L", unitID = string22_1.UnitID, percentage = 1 });


            allUnitList.Add(new Unit() { ParentUnitID = 0, UnitID = 5, UnitName = "Tower3", UnitType = FIFPConstants.UNITTYPE_TOWER, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            allUnitList.Add(new Unit() { ParentUnitID = 5, UnitID = 15, UnitName = "Well31", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            allUnitList.Add(new Unit() { ParentUnitID = 5, UnitID = 16, UnitName = "Well32", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            Unit string31_1 = new Unit() { ParentUnitID = 15, UnitID = 44, UnitName = "String31/1", UnitType = FIFPConstants.UNITTYPE_STRING, FluidType = FIFPConstants.FLUIDTYPE_OIL };
            Unit string32_1 = new Unit() { ParentUnitID = 16, UnitID = 45, UnitName = "String32/1", UnitType = FIFPConstants.UNITTYPE_STRING, FluidType = FIFPConstants.FLUIDTYPE_OIL };
            allUnitList.Add(string31_1);
            allUnitList.Add(string32_1);
            unitCapacityDictionary.Add(getKeyForStringCapacity(string31_1.UnitID, new DateTime(2013, 1, 1)), 100);
            unitCapacityDictionary.Add(getKeyForStringCapacity(string32_1.UnitID, new DateTime(2013, 1, 1)), 150);
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D4U", unitID=string31_1.UnitID, percentage = 1 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D4L", unitID=string32_1.UnitID, percentage = 1 });


            allUnitList.Add(new Unit() { ParentUnitID = 5, UnitID = 6, UnitName = "Pipeline3", UnitType = FIFPConstants.UNITTYPE_PIPELINE, Capacity = 300, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            allUnitList.Add(new Unit() { ParentUnitID = 6, UnitID = 7, UnitName = "Tower4", UnitType = FIFPConstants.UNITTYPE_TOWER, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            allUnitList.Add(new Unit() { ParentUnitID = 7, UnitID = 17, UnitName = "Well41", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            allUnitList.Add(new Unit() { ParentUnitID = 7, UnitID = 18, UnitName = "Well42", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            Unit string41_1 = new Unit() { ParentUnitID = 17, UnitID = 46, UnitName = "String41/1", UnitType = FIFPConstants.UNITTYPE_STRING, FluidType = FIFPConstants.FLUIDTYPE_OIL };
            Unit string42_1 = new Unit() { ParentUnitID = 18, UnitID = 47, UnitName = "String42/1", UnitType = FIFPConstants.UNITTYPE_STRING, FluidType = FIFPConstants.FLUIDTYPE_OIL };
            allUnitList.Add(string41_1);
            allUnitList.Add(string42_1);
            unitCapacityDictionary.Add(getKeyForStringCapacity(string41_1.UnitID, new DateTime(2013, 1, 1)), 100);
            unitCapacityDictionary.Add(getKeyForStringCapacity(string42_1.UnitID, new DateTime(2013, 1, 1)), 100);
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D5", unitID=string41_1.UnitID, percentage = 1 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D2", unitID=string42_1.UnitID, percentage = 1 });


            allUnitList.Add(new Unit() { ParentUnitID = 5, UnitID = 8, UnitName = "Pipeline4", UnitType = FIFPConstants.UNITTYPE_PIPELINE, Capacity = 200, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            allUnitList.Add(new Unit() { ParentUnitID = 8, UnitID = 9, UnitName = "Tower5", UnitType = FIFPConstants.UNITTYPE_TOWER, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            allUnitList.Add(new Unit() { ParentUnitID = 9, UnitID = 19, UnitName = "Well51", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            Unit string51_1 = new Unit() { ParentUnitID = 19, UnitID = 48, UnitName = "String51/1", UnitType = FIFPConstants.UNITTYPE_STRING, FluidType = FIFPConstants.FLUIDTYPE_OIL };
            allUnitList.Add(string51_1);
            unitCapacityDictionary.Add(getKeyForStringCapacity(string51_1.UnitID, new DateTime(2013, 1, 1)), 200);
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D3L", unitID=string51_1.UnitID, percentage = 1 });


            allUnitList.Add(new Unit() { ParentUnitID = 7, UnitID = 10, UnitName = "Pipeline5", UnitType = FIFPConstants.UNITTYPE_PIPELINE, Capacity = 400, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            allUnitList.Add(new Unit() { ParentUnitID = 10, UnitID = 11, UnitName = "Tower6", UnitType = FIFPConstants.UNITTYPE_TOWER, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            allUnitList.Add(new Unit() { ParentUnitID = 11, UnitID = 20, UnitName = "Well61", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            allUnitList.Add(new Unit() { ParentUnitID = 11, UnitID = 21, UnitName = "Well62", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            Unit string61_1 = new Unit() { ParentUnitID = 20, UnitID = 49, UnitName = "String61/1", UnitType = FIFPConstants.UNITTYPE_STRING, FluidType = FIFPConstants.FLUIDTYPE_OIL };
            Unit string62_1 = new Unit() { ParentUnitID = 21, UnitID = 50, UnitName = "String62/1", UnitType = FIFPConstants.UNITTYPE_STRING, FluidType = FIFPConstants.FLUIDTYPE_OIL };
            allUnitList.Add(string61_1);
            allUnitList.Add(string62_1);
            unitCapacityDictionary.Add(getKeyForStringCapacity(string61_1.UnitID, new DateTime(2013, 1, 1)), 200);
            unitCapacityDictionary.Add(getKeyForStringCapacity(string62_1.UnitID, new DateTime(2013, 1, 1)), 150);
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D3L", unitID=string61_1.UnitID, percentage = 1 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D4U", unitID=string62_1.UnitID, percentage = 1 });


            allUnitList.Add(new Unit() { ParentUnitID = 6, UnitID = 22, UnitName = "Pipeline6", UnitType = FIFPConstants.UNITTYPE_PIPELINE, Capacity = 100, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            allUnitList.Add(new Unit() { ParentUnitID = 22, UnitID = 23, UnitName = "Tower7", UnitType = FIFPConstants.UNITTYPE_TOWER, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            allUnitList.Add(new Unit() { ParentUnitID = 23, UnitID = 24, UnitName = "Well71", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_OIL });
            Unit string71_1 = new Unit() { ParentUnitID = 24, UnitID = 51, UnitName = "String71/1", UnitType = FIFPConstants.UNITTYPE_STRING, FluidType = FIFPConstants.FLUIDTYPE_OIL };
            Unit string72_1 = new Unit() { ParentUnitID = 24, UnitID = 52, UnitName = "String71/2", UnitType = FIFPConstants.UNITTYPE_STRING, FluidType = FIFPConstants.FLUIDTYPE_OIL };
            allUnitList.Add(string71_1);
            allUnitList.Add(string72_1);
            unitCapacityDictionary.Add(getKeyForStringCapacity(string71_1.UnitID, new DateTime(2013, 1, 1)), 25);
            unitCapacityDictionary.Add(getKeyForStringCapacity(string72_1.UnitID, new DateTime(2013, 1, 1)), 25);
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB C",   unitID=string71_1.UnitID, percentage = 1 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "NON ARAB", unitID = string72_1.UnitID, percentage = 1 });


            //Water Units
            allUnitList.Add(new Unit() { ParentUnitID = -1, UnitID = 100, UnitName = "Radial2", UnitType = FIFPConstants.UNITTYPE_RADIAL, Capacity = 900, FluidType = FIFPConstants.FLUIDTYPE_WATER });

            allUnitList.Add(new Unit() { ParentUnitID = 100, UnitID = 101, UnitName = "Tower11", UnitType = FIFPConstants.UNITTYPE_TOWER, FluidType = FIFPConstants.FLUIDTYPE_WATER });
            allUnitList.Add(new Unit() { ParentUnitID = 101, UnitID = 102, UnitName = "Well111", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_WATER });
            Unit string111_1 = new Unit() { ParentUnitID = 102, UnitID = 1021, UnitName = "String111/1", UnitType = FIFPConstants.UNITTYPE_STRING, FluidType = FIFPConstants.FLUIDTYPE_WATER };
            allUnitList.Add(string111_1);
            unitCapacityDictionary.Add(getKeyForStringCapacity(string111_1.UnitID, new DateTime(2013, 1, 1)), 100);
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB C1", unitID = string111_1.UnitID, percentage = 0.3 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB C2", unitID = string111_1.UnitID, percentage = 0.3 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB C3", unitID = string111_1.UnitID, percentage = 0.4 });

            allUnitList.Add(new Unit() { ParentUnitID = 101, UnitID = 103, UnitName = "Pipeline21", UnitType = FIFPConstants.UNITTYPE_PIPELINE, Capacity = 300, FluidType = FIFPConstants.FLUIDTYPE_WATER });
            allUnitList.Add(new Unit() { ParentUnitID = 103, UnitID = 104, UnitName = "Tower21", UnitType = FIFPConstants.UNITTYPE_TOWER, FluidType = FIFPConstants.FLUIDTYPE_WATER });
            allUnitList.Add(new Unit() { ParentUnitID = 104, UnitID = 105, UnitName = "Well211", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_WATER });
            allUnitList.Add(new Unit() { ParentUnitID = 104, UnitID = 113, UnitName = "Well212", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_WATER });
            Unit string211_1 = new Unit() { ParentUnitID = 105, UnitID = 1051, UnitName = "String211/1", UnitType = FIFPConstants.UNITTYPE_STRING, FluidType = FIFPConstants.FLUIDTYPE_WATER };
            Unit string212_1 = new Unit() { ParentUnitID = 113, UnitID = 1131, UnitName = "String212/1", UnitType = FIFPConstants.UNITTYPE_STRING, FluidType = FIFPConstants.FLUIDTYPE_WATER };
            allUnitList.Add(string211_1);
            allUnitList.Add(string212_1);
            unitCapacityDictionary.Add(getKeyForStringCapacity(string211_1.UnitID, new DateTime(2013, 1, 1)), 100);
            unitCapacityDictionary.Add(getKeyForStringCapacity(string212_1.UnitID, new DateTime(2013, 1, 1)), 100);
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D2",  unitID = string211_1.UnitID, percentage = 0.4 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D3U", unitID = string211_1.UnitID, percentage = 0.6 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D3L", unitID = string211_1.UnitID, percentage = 1 });

            allUnitList.Add(new Unit() { ParentUnitID = 104, UnitID = 106, UnitName = "Pipeline22", UnitType = FIFPConstants.UNITTYPE_PIPELINE, Capacity = 200, FluidType = FIFPConstants.FLUIDTYPE_WATER });
            allUnitList.Add(new Unit() { ParentUnitID = 106, UnitID = 107, UnitName = "Tower31", UnitType = FIFPConstants.UNITTYPE_TOWER, FluidType = FIFPConstants.FLUIDTYPE_WATER });
            allUnitList.Add(new Unit() { ParentUnitID = 107, UnitID = 108, UnitName = "Well311", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_WATER });
            Unit string311_1 = new Unit() { ParentUnitID = 108, UnitID = 1081, UnitName = "String311/1", UnitType = FIFPConstants.UNITTYPE_STRING, FluidType = FIFPConstants.FLUIDTYPE_WATER };
            allUnitList.Add(string311_1);
            unitCapacityDictionary.Add(getKeyForStringCapacity(string311_1.UnitID, new DateTime(2013, 1, 1)), 100);
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D3U", unitID = string311_1.UnitID, percentage = 0.1 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D3L", unitID = string311_1.UnitID, percentage = 0.3 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D4U", unitID = string311_1.UnitID, percentage = 0.6 });

            allUnitList.Add(new Unit() { ParentUnitID = 101, UnitID = 109, UnitName = "Pipeline23", UnitType = FIFPConstants.UNITTYPE_PIPELINE, Capacity = 200, FluidType = FIFPConstants.FLUIDTYPE_WATER });
            allUnitList.Add(new Unit() { ParentUnitID = 109, UnitID = 110, UnitName = "Tower41", UnitType = FIFPConstants.UNITTYPE_TOWER, FluidType = FIFPConstants.FLUIDTYPE_WATER });
            allUnitList.Add(new Unit() { ParentUnitID = 110, UnitID = 111, UnitName = "Well411", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_WATER });
            allUnitList.Add(new Unit() { ParentUnitID = 110, UnitID = 112, UnitName = "Well412", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_WATER });
            Unit string411_1 = new Unit() { ParentUnitID = 111, UnitID = 1111, UnitName = "String411/1", UnitType = FIFPConstants.UNITTYPE_STRING, FluidType = FIFPConstants.FLUIDTYPE_WATER };
            Unit string412_1 = new Unit() { ParentUnitID = 112, UnitID = 1121, UnitName = "String412/1", UnitType = FIFPConstants.UNITTYPE_STRING, FluidType = FIFPConstants.FLUIDTYPE_WATER };
            allUnitList.Add(string411_1);
            allUnitList.Add(string412_1);
            unitCapacityDictionary.Add(getKeyForStringCapacity(string411_1.UnitID, new DateTime(2013, 1, 1)), 100);
            unitCapacityDictionary.Add(getKeyForStringCapacity(string412_1.UnitID, new DateTime(2013, 1, 1)), 100);
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D4L",unitID=string411_1.UnitID, percentage = 0.7 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D5", unitID = string411_1.UnitID, percentage = 0.3 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D2", unitID = string412_1.UnitID, percentage = 0.2 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D5", unitID = string412_1.UnitID, percentage = 0.2 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D3U", unitID = string412_1.UnitID, percentage = 0.3 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D4U", unitID = string412_1.UnitID, percentage = 0.3 });

            //water facility
            allUnitList.Add(new Unit() { UnitID = 201, UnitName = "US-WI-MODULE-1", UnitType = FIFPConstants.UNITTYPE_MODULE, Capacity = 300, FluidType = FIFPConstants.FLUIDTYPE_WATER });
            allUnitList.Add(new Unit() { UnitID = 202, UnitName = "US-WI-MODULE-2", UnitType = FIFPConstants.UNITTYPE_MODULE, Capacity = 300, FluidType = FIFPConstants.FLUIDTYPE_WATER });
            //allUnitList.Add(new Unit() { UnitID = 203, UnitName = "US-WI-MODULE-3", UnitType = FIFPConstants.UNITTYPE_WIMODULE, Capacity = 300, FluidType = FIFPConstants.WATER });
           

            //gas facility
            allUnitList.Add(new Unit() { UnitID = 301, UnitName = "GIC_Compressor_1", UnitType = FIFPConstants.UNITTYPE_COMPRESSOR, Capacity = 400, FluidType = FIFPConstants.FLUIDTYPE_GAS });
            allUnitList.Add(new Unit() { UnitID = 302, UnitName = "GIC_Compressor_2", UnitType = FIFPConstants.UNITTYPE_COMPRESSOR, Capacity = 400, FluidType = FIFPConstants.FLUIDTYPE_GAS });
            
            //gas units
            allUnitList.Add(new Unit() { ParentUnitID = -1, UnitID = 401, UnitName = "Radial3", UnitType = FIFPConstants.UNITTYPE_RADIAL, Capacity = 500, FluidType = FIFPConstants.FLUIDTYPE_GAS });
            allUnitList.Add(new Unit() { ParentUnitID = 401, UnitID = 402, UnitName = "Tower111", UnitType = FIFPConstants.UNITTYPE_TOWER, FluidType = FIFPConstants.FLUIDTYPE_GAS });
            Unit well1111 = new Unit() { ParentUnitID = 402, UnitID = 403, UnitName = "Well1111", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_GAS };
            allUnitList.Add(well1111);
            unitCapacityDictionary.Add(getKeyForStringCapacity(well1111.UnitID, new DateTime(2013, 1, 1)), 300);
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D2",  unitID = well1111.UnitID, percentage = 0.1 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D3U", unitID = well1111.UnitID, percentage = 0.3 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D3L", unitID = well1111.UnitID, percentage = 0.2 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D4U", unitID = well1111.UnitID, percentage = 0.25 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D4L", unitID = well1111.UnitID, percentage = 0.15 });

            allUnitList.Add(new Unit() { ParentUnitID = -1, UnitID = 404, UnitName = "Radial4", UnitType = FIFPConstants.UNITTYPE_RADIAL, Capacity = 500, FluidType = FIFPConstants.FLUIDTYPE_GAS });
            allUnitList.Add(new Unit() { ParentUnitID = 404, UnitID = 405, UnitName = "Tower112", UnitType = FIFPConstants.UNITTYPE_TOWER, FluidType = FIFPConstants.FLUIDTYPE_GAS });
            Unit well1121 = new Unit() { ParentUnitID = 405, UnitID = 406, UnitName = "Well1121", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_GAS };
            Unit well1122 = new Unit() { ParentUnitID = 405, UnitID = 407, UnitName = "Well1122", UnitType = FIFPConstants.UNITTYPE_WELL, FluidType = FIFPConstants.FLUIDTYPE_GAS };                      
            allUnitList.Add(well1121);
            allUnitList.Add(well1122);
            unitCapacityDictionary.Add(getKeyForStringCapacity(well1121.UnitID, new DateTime(2013, 1, 1)), 300);
            unitCapacityDictionary.Add(getKeyForStringCapacity(well1122.UnitID, new DateTime(2013, 1, 1)), 200);
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D2", unitID = well1121.UnitID, percentage = 0.5 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D3U", unitID = well1121.UnitID, percentage = 0.2 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D4U", unitID = well1121.UnitID, percentage = 0.3 });

            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D3L", unitID = well1122.UnitID, percentage = 0.4 });
            AddtoLayerUnitsDictionary(new UnitLayer { layerName = "ARAB D4L", unitID = well1122.UnitID, percentage = 0.6 });

            //generate child
            GetUnitNameList(AllUnitList.Where(p => p.UnitType == FIFPConstants.UNITTYPE_RADIAL).ToList());    
       
            //plans
            FIFPAppointment appointment1 = new FIFPAppointment();
            appointment1.PlanID = 1;
            appointment1.UnitName = "String411/1";
            appointment1.UnitID = 1111;
            appointment1.UnitType = FIFPConstants.UNITTYPE_STRING;
            appointment1.FluidType = FIFPConstants.FLUIDTYPE_WATER;
            appointment1.Start = new DateTime(2013, 1, 5);
            appointment1.End = new DateTime(2013, 1, 10);

            FIFPAppointment appointment2 = new FIFPAppointment();
            appointment2.PlanID = 2;
            appointment2.UnitName = "Pipeline23";
            appointment2.UnitID = 109;
            appointment2.UnitType = FIFPConstants.UNITTYPE_PIPELINE;
            appointment2.FluidType = FIFPConstants.FLUIDTYPE_WATER;
            appointment2.Start = new DateTime(2013, 1, 8);
            appointment2.End = new DateTime(2013, 1, 15);

            FIFPAppointment appointment3 = new FIFPAppointment();
            appointment3.PlanID = 3;
            appointment3.UnitName = "US-WI-MODULE-1";
            appointment3.UnitID = 201;
            appointment3.UnitType = FIFPConstants.UNITTYPE_MODULE;
            appointment3.FluidType = FIFPConstants.FLUIDTYPE_WATER;
            appointment3.Start = new DateTime(2013, 1, 12);
            appointment3.End = new DateTime(2013, 1, 25);

            FIFPAppointment appointment4 = new FIFPAppointment();
            appointment4.PlanID = 4;
            appointment4.UnitName = "String21/1";
            appointment4.UnitID = 42;
            appointment4.UnitType = FIFPConstants.UNITTYPE_STRING;
            appointment4.FluidType = FIFPConstants.FLUIDTYPE_OIL;
            appointment4.Start = new DateTime(2013, 1, 1);
            appointment4.End = new DateTime(2013, 1, 10);

            FIFPAppointment appointment5 = new FIFPAppointment();
            appointment5.PlanID = 5;
            appointment5.UnitName = "String51/1";
            appointment5.UnitID = 48;
            appointment5.UnitType = FIFPConstants.UNITTYPE_STRING;
            appointment5.FluidType = FIFPConstants.FLUIDTYPE_OIL;
            appointment5.Start = new DateTime(2013, 1, 1);
            appointment5.End = new DateTime(2013, 1, 5);

            FIFPAppointment appointment6 = new FIFPAppointment();
            appointment6.PlanID = 6;
            appointment6.UnitName = "GIC_Compressor_1";
            appointment6.UnitID = 301;
            appointment6.UnitType = FIFPConstants.UNITTYPE_COMPRESSOR;
            appointment6.FluidType = FIFPConstants.FLUIDTYPE_GAS;
            appointment6.Start = new DateTime(2013, 1, 25);
            appointment6.End = new DateTime(2013, 1, 30);


            FIFPAppointment appointment7 = new FIFPAppointment();
            appointment7.PlanID = 7;
            appointment7.UnitName = "Radial1";
            appointment7.UnitID = 401;
            appointment7.UnitType = FIFPConstants.UNITTYPE_RADIAL;
            appointment7.FluidType = FIFPConstants.FLUIDTYPE_GAS;
            appointment7.Start = new DateTime(2013, 1, 29);
            appointment7.End = new DateTime(2013, 1, 30);

            importPlans.Add(appointment1);
            importPlans.Add(appointment2);
            importPlans.Add(appointment3);
            importPlans.Add(appointment4);
            importPlans.Add(appointment5);
            importPlans.Add(appointment6);
            importPlans.Add(appointment7);
            //........... add all linked shutdown later
            InitUnitShutdownDictionary(importPlans);
        }

        private void InitUnitShutdownDictionary(List<FIFPAppointment> importPlans)
        {
            foreach (FIFPAppointment appointment in importPlans)
            {
                if (unitShutdownPlanDictionary.ContainsKey(appointment.UnitID))
                {
                    List<FIFPAppointment> l = unitShutdownPlanDictionary[appointment.UnitID];
                    l.Add(appointment);
                }
                else
                {
                    List<FIFPAppointment> l = new List<FIFPAppointment>();
                    l.Add(appointment);
                    unitShutdownPlanDictionary.Add(appointment.UnitID, l);
                }
            }
        }

        private void AddtoLayerUnitsDictionary(UnitLayer unitLayer)
        {
            if (unitLayer.layerName.Contains("ARAB C"))
            {
                if (layerUnitsDictionary.ContainsKey("ARAB C"))
                {
                    List<UnitLayer> l = layerUnitsDictionary["ARAB C"];
                    l.Add(unitLayer);
                }
                else
                {
                    List<UnitLayer> l = new List<UnitLayer>();
                    l.Add(unitLayer);
                    layerUnitsDictionary.Add("ARAB C", l);
                }
            }
            else
            {
                if (layerUnitsDictionary.ContainsKey(unitLayer.layerName))
                {
                    List<UnitLayer> l = layerUnitsDictionary[unitLayer.layerName];
                    l.Add(unitLayer);
                }
                else
                {
                    List<UnitLayer> l = new List<UnitLayer>();
                    l.Add(unitLayer);
                    layerUnitsDictionary.Add(unitLayer.layerName, l);
                }
            }
        }

        //generate childUnitList in Unit class
        private void GetUnitNameList(List<Unit> lstUnits)
        {
            foreach (Unit unit in lstUnits)
            {
                unit.ChildUnitList = allUnitList.Where(p => p.ParentUnitID == unit.UnitID).ToList<Unit>();
                GetUnitNameList(unit.ChildUnitList);
            }           
        }
        #endregion
    }
}